package com.heima.media.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.dto.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.minio.MinIOService;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.media.dto.ArticleDto;
import com.heima.media.dto.ContentDto;
import com.heima.media.dto.ImageDto;
import com.heima.media.entity.WmChannel;
import com.heima.media.entity.WmNews;
import com.heima.media.entity.WmSensitive;
import com.heima.media.entity.WmUser;
import com.heima.media.feign.ArticleClient;
import com.heima.media.service.*;
import org.apache.commons.io.IOUtils;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service
public class AuditServiceImpl implements IAuditService {
    @Autowired
    private GreenImageScan imageScan;
    @Autowired
    private GreenTextScan textScan;
    @Autowired
    private IWmNewsService wmNewsService;
    @Autowired
    private MinIOService minIOService;
    @Autowired
    private ArticleClient articleClient;
    @Autowired
    private IWmUserService iWmUserService;
    @Autowired
    private IWmChannelService iWmChannelService;
    @Autowired
    IWmSensitiveService iWmSensitiveService;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 文章审核
     *
     * @param wmNews
     */
    @Async //此方法需要使用线程池
    @Override
    public void audit(WmNews wmNews) {
//        判断当前文章状态
//        如果状态是8 开始进入文章发布
        if (wmNews.getStatus() == 8) {
            publishArticle(wmNews);
            return;
        }
//        如果是1 进入自动审核流程
//        提取内容中的文本和图片
        Map<String, Object> map = getTextAndImageFromContent(wmNews.getContent());
//          --获取文本内容
        String text = (String) map.get("text");
//          --获取图片内容
        List<String> images = (List<String>) map.get("image");
//        自管理敏感词审核
        boolean selfResult = checkSelf(wmNews, text);
        if (!selfResult) return;
//        调用阿里云文本审核
        boolean textResult = checkText(wmNews, text);
        if (!textResult) return;
//        调用阿里图片审核
        boolean imsgeResult = checkImage(wmNews, images);
        if (!imsgeResult) return;
//        调用文章服务保存接口
//        判断有没有到发布时间，如果没到，将文章状态改为8，审核通过（待发布），发送演示任务消息
        if (wmNews.getPublishTime().getTime() > System.currentTimeMillis()) {
            sendDelayMessage(wmNews);
        } else {
            publishArticle(wmNews);
        }
    }


    /**
     * 发送延时任务
     *
     * @param wmNews
     */
//    TODO 延时发布有bug
    private void sendDelayMessage(WmNews wmNews) {
        wmNews.setStatus(8);
        wmNewsService.updateById(wmNews);
        // 创建阻塞队列
        RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue("article_publish");
        // 创建延迟队列
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
        // 发送延迟消息 参数为:  1 发送的消息  2 延迟时间  3 时间单位
        // 需要延迟的时间为  发布时间 - 当前系统时间
        long time = wmNews.getPublishTime().getTime() - System.currentTimeMillis();
        delayedQueue.offer(JSON.toJSONString(wmNews), time, TimeUnit.MILLISECONDS);
        System.out.println("消息发送完成" + new Date());

    }

    /**
     * 自管理敏感词审核
     *
     * @param wmNews
     * @param text
     * @return
     */
    private boolean checkSelf(WmNews wmNews, String text) {
//        第一次使用是将数据库中的敏感词查出来，进行map初始化
        if (SensitiveWordUtil.dictionaryMap.size() == 0) {
//          从数据库查询
            LambdaQueryWrapper<WmSensitive> queryWrapper = new LambdaQueryWrapper<>();
//            指定查询那一列
            queryWrapper.select(WmSensitive::getSensitives);
            List<String> woeds = iWmSensitiveService.listObjs(queryWrapper, o -> o.toString());
//            初始化map
            SensitiveWordUtil.initMap(woeds);
        }
//        文本审核 : 标题+标签+正文中的文字
        String content = wmNews.getTitle() + wmNews.getLabels() + text;
        Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
//        判断结果
        if (map.size() > 0) {
            wmNews.setStatus(2);
//            获取所有的key
            Set<String> keySet = map.keySet();
//            String lable = String.join("," + keySet);
            String lable = keySet.toString();
            wmNews.setReason("自管理敏感词审核失败" + lable);
//            更新状态
            wmNewsService.updateById(wmNews);
            return false;
        } else {
            return true;
        }

    }

    /**
     * 发布文章
     *
     * @param wmNews
     */
    private void publishArticle(WmNews wmNews) {
        ArticleDto dto = new ArticleDto();
//        设置文章数据
        BeanUtils.copyProperties(wmNews, dto);
        dto.setId(null);
//        获取作者ID 查询用户
        WmUser wmUser = iWmUserService.getById(wmNews.getUserId());
        dto.setAuthorId(wmUser.getApAuthorId());
        dto.setAuthorName(wmUser.getName());
//         查询频道信息
        WmChannel wmChannel = iWmChannelService.getById(wmNews.getChannelId());
        dto.setChannelName(wmChannel.getName());
        dto.setLayout(wmNews.getType());
        dto.setFlag(0);
//        封面图片需要转换
        List<ImageDto> imageDtos = JSON.parseArray(wmNews.getImages(), ImageDto.class);
        List<String> list = new ArrayList<>();
        for (ImageDto imageDto : imageDtos) {
            list.add(imageDto.getUrl());
        }
//        将集合转换常逗号分割的字符串
        String join = String.join(",", list);
        dto.setImages(join);
//        调用远程接口
        ResponseResult<Long> longResponseResult = articleClient.saveArticle(dto);
//        判断结果
        if (longResponseResult.getCode().equals(AppHttpCodeEnum.SUCCESS.getCode())) {
            Long articleId = longResponseResult.getData();
//            将文章ID写回到自媒体文章表，并且修改状态为9
            wmNews.setArticleId(articleId);
            wmNews.setStatus(9);
            wmNewsService.updateById(wmNews);

        }
    }

    /**
     * 阿里云图片审核
     *
     * @param wmNews
     * @param images
     * @return
     */
    private boolean checkImage(WmNews wmNews, List<String> images) {
//        需要审核的图片包括内容中的和封面中的
//        获取封面图片
        List<ImageDto> imageDtoList = JSON.parseArray(wmNews.getImages(), ImageDto.class);
//        遍历
        for (ImageDto imageDto : imageDtoList) {
//            --判断封面图片是否存在于内容图片中
            if (!images.contains(imageDto.getUrl())) {
//              ---将封面图片加入最终要审核的图片结合中
//                ----将无图的排除
                if (!StringUtils.isEmpty(imageDto.getUrl())) {
                    images.add(imageDto.getUrl());
                }
            }
        }
//        判断集合中是否有图片
        if (images.size() == 0) {
            return true;
        }
        boolean ret = false;

        try {
//        MinIO没有外网地址，需要线下下载图片
            List<byte[]> imagesList = new ArrayList<>();
//        --遍历图片集合，到MinIO中下载
            for (String image : images) {
                InputStream download = minIOService.download(image);
//        转换成字节数组
                byte[] bytes = IOUtils.toByteArray(download);
                imagesList.add(bytes);
            }
            Map map = imageScan.imageScan(imagesList);
            //            分析结果中的建议 suggestion
            String suggestion = (String) map.get("suggestion");
//            suggestion的结果
            switch (suggestion) {
                case "pass":
//                    psss:结果正常、
                    ret = true;
                    break;
                case "block":
//                    block 结果违规，修改文章状态为2，记录失败的原因
                    wmNews.setStatus(2);
                    String label = (String) map.get("lable");
                    wmNews.setReason("阿里云图片审核失败" + label);
//                    更新状态
                    wmNewsService.updateById(wmNews);
                    break;
                case "review":
//                    review 结果不确定，需要人工审核，修改文章状态为3
                    wmNews.setStatus(3);
                    wmNewsService.updateById(wmNews);
                    break;

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * 阿里云文本审核
     *
     * @param wmNews
     * @param text
     * @return
     */
    private boolean checkText(WmNews wmNews, String text) {
        boolean fiag = false;
//        文本审核 : 标题+标签+正文中的文字
        String content = wmNews.getTitle() + wmNews.getLabels() + text;
        try {
            Map map = textScan.greenTextScan(content);
//            分析结果中的建议 suggestion
            String suggestion = (String) map.get("suggestion");
//            suggestion的结果
            switch (suggestion) {
                case "pass":
//                    psss:结果正常、
                    fiag = true;
                    break;
                case "block":
//                    block 结果违规，修改文章状态为2，记录失败的原因
                    wmNews.setStatus(2);
                    String label = (String) map.get("lable");
                    wmNews.setReason("阿里云文本审核失败" + label);
//                    更新状态
                    wmNewsService.updateById(wmNews);
                    break;
                case "review":
//                    review 结果不确定，需要人工审核，修改文章状态为3
                    wmNews.setStatus(3);
                    wmNewsService.updateById(wmNews);
                    break;

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fiag;
    }

    /**
     * 提取内容中的文本和图片
     *
     * @param content
     * @return
     */
    private Map<String, Object> getTextAndImageFromContent(String content) {
//        定义返回结果
        Map<String, Object> map = new HashMap<>();
//        定义文本集合
        StringBuilder sb = new StringBuilder();
//        定义图片集合
        List<String> images = new ArrayList<>();
//        将内容抓换成contentDto集合
        List<ContentDto> contentDtos = JSON.parseArray(content, ContentDto.class);
        for (ContentDto contentDto : contentDtos) {
            if (contentDto.getType().equals("text")) {
                sb.append(contentDto.getValue());
            } else {
                images.add(contentDto.getValue());
            }
        }

        map.put("text", sb.toString());
        map.put("image", images);
        return map;
    }
}
